/*
 * Copyright 2011 Vlad Goldman.  All rights reserved.
  
   This file is part of GooOutSync - Google Outlook Sync application
  
    Project page: http://goooutsync.googlecode.com

    GooOutSync is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    GooOutSync is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with GooOutSync.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.vgold.goooutsync;

import java.awt.AWTException;
import java.awt.Image;
import java.awt.SystemTray;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.URL;

import javax.swing.ImageIcon;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.SwingWorker;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

import org.apache.log4j.Logger;

import com.google.gdata.util.ServiceException;

public final class GooutSyncGUI implements Runnable, ActionListener {

	String trayIconPath = "resources/images/googlenew3.gif";
	String traySyncedIconPath = "resources/images/googlenew2synced.gif";
	// String traySyncIconPath = "resources/images/sync.png";
	String traySyncIconPath = "resources/images/googlenew2anim_sync.gif";
	String traySyncFailPath = "resources/images/googlenew2fail.gif";
	String exitIconPath = "resources/images/exit.gif";
	String syncIconPath = "resources/images/sync.png";
	String aboutIconPath = "resources/images/about.gif";
	String settingsIconPath = "resources/images/settings.gif";
	String snoozeIconPath = "resources/images/snooze.gif";
	String snoozeTrayIconPath = "resources/images/googlenew2pause.gif";
	ActionListener listener;
	ActionListener trayListener;
	JXTrayIcon trayIcon;
	SystemTray tray;
	Image trayImage;
	JPopupMenu popup;
	GooOutSync gooOutSync;
	private GooOutSettings gooOutSettings;
	private Logger logger;
	private boolean isInSnooze = false;
	private int timeBetweenSync;
	GooOutSyncPreferences preferences;
	private boolean showPopups;
	SwingWorker syncTask;
	Timer timer;
	private GooOutSyncStatus gooOutSyncStatus;

	public GooutSyncGUI(GooOutSync gooOutSync) throws GooOutSyncException {
		this.gooOutSync = gooOutSync;
		logger = Logger.getLogger(GooutSyncGUI.class);
		loadPreferences();

	}

	private void loadPreferences() {
		preferences = GooOutSyncPreferences.getGooOutSyncPreferences();
		timeBetweenSync = Integer.valueOf(preferences
				.getPreference(GooOutSyncPreferences.Prefs.time_between_sync)) * 60000;
		showPopups = Boolean.valueOf(preferences
				.getPreference(GooOutSyncPreferences.Prefs.show_popups));
	}

	private void setLookAndFeel() {
		try {
			UIManager
					.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
			// UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
		} catch (UnsupportedLookAndFeelException ex) {
			ex.printStackTrace();
		} catch (IllegalAccessException ex) {
			ex.printStackTrace();
		} catch (InstantiationException ex) {
			ex.printStackTrace();
		} catch (ClassNotFoundException ex) {
			ex.printStackTrace();
		}
		/* Turn off metal's use of bold fonts */
		UIManager.put("swing.boldMetal", Boolean.FALSE);

	}

	private void createMenu() {
		// create a action listener to listen for default action executed on the
		// tray icon
		listener = new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				JMenuItem item = (JMenuItem) e.getSource();
				// TrayIcon.MessageType type = null;
				logger.info("Click menu event: " + item.getLabel());
				if ("Exit".equals(item.getLabel())) {
					// type = TrayIcon.MessageType.ERROR;
					displaySimpleMessage("GooOutSync", "Exiting...");

					// gooOutSync.destroy();
					destroy();
				}
				if ("Sync Now!".equals(item.getLabel())) {
					if (gooOutSync.isInSync()) {
						logger.info("Attempted forced Sync, while sync is already running");
						displaySimpleMessage("GooOutSync",
								"Synchronizattion is already running...");
						return;
					}
					syncTask = new SyncTask();
					syncTask.execute();
				}
				if ("Settings".equals(item.getLabel())) {
					gooOutSettings.setVisible(true);
				}

				if ("About".equals(item.getLabel())) {
					gooOutSettings.setVisible(true);
					gooOutSettings.switchToAboutTab();
				}

				if ("Snooze Sync".equals(item.getLabel())) {
					if (timer.isRunning()) {
						displaySimpleMessage("GooOutSync", "ZzzZZzzz...");
						setSnoozeIcon();
						isInSnooze = true;
						timer.stop();
					} else {
						displaySimpleMessage("GooOutSync", "Sync is on...");
						setDefaultIcon();
						isInSnooze = false;
						timer.start();
					}
				}

			}
		};
		ActionListener exitListener = new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				// TODO Auto-generated method stub

			}
		};
		trayListener = new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				// TODO Auto-generated method stub
				logger.info("Tray listener event: " + e.getSource().toString());
			}
		};
		// create a popup menu
		popup = new JPopupMenu();
		// create menu item for the default action

		JMenuItem syncNowItem = new JMenuItem("Sync Now!", createImageIcon(
				syncIconPath, "Sync"));
		// syncNowItem.setShortcut(new MenuShortcut(KeyEvent.VK_S, false));
		popup.add(syncNowItem);
		JCheckBoxMenuItem pauseSyncItem = new JCheckBoxMenuItem("Snooze Sync",
				createImageIcon(snoozeIconPath, "Snooze"));
		JMenuItem exitItem = new JMenuItem("Exit", createImageIcon(
				exitIconPath, "Exit"));
		JMenuItem settingsItem = new JMenuItem("Settings", createImageIcon(
				settingsIconPath, "Settings"));
		JMenuItem aboutItem = new JMenuItem("About", createImageIcon(
				aboutIconPath, "About"));
		syncNowItem.addActionListener(listener);
		exitItem.addActionListener(listener);
		settingsItem.addActionListener(listener);
		pauseSyncItem.addActionListener(listener);
		aboutItem.addActionListener(listener);
		popup.add(aboutItem);
		popup.add(pauseSyncItem);
		popup.add(settingsItem);
		popup.add(exitItem);
		popup.addSeparator();
		popup.add(syncNowItem);

		// / ... add other items
		// construct a TrayIcon

	}

	public synchronized void startSyncIcon() {
		Image traySyncImage = createImage(traySyncIconPath, "TraySyncIcon");
		trayIcon.setImage(traySyncImage);
	}

	public synchronized void setSyncFailIcon() {
		Image traySyncImage = createImage(traySyncFailPath, "TraySyncIcon");
		trayIcon.setImage(traySyncImage);
	}

	public void finishSyncIcon() {
		Image traySyncImage = createImage(traySyncedIconPath, "TraySyncIcon");
		trayIcon.setImage(traySyncImage);
	}

	public void setDefaultIcon() {
		Image traySyncImage = createImage(trayIconPath, "TraySyncIcon");
		trayIcon.setImage(traySyncImage);
	}

	public void setSnoozeIcon() {
		Image traySyncImage = createImage(snoozeTrayIconPath, "TraySyncIcon");
		trayIcon.setImage(traySyncImage);
	}

	protected static Image createImage(String path, String description) {
		URL imageURL = GooutSyncGUI.class.getResource(path);

		if (imageURL == null) {
			System.err.println("Resource not found: " + path);
			return null;
		} else {
			return (new ImageIcon(imageURL, description)).getImage();
		}
	}

	public Image getTrayIcon() {
		return createImage(trayIconPath, "TrayIcon");
	}

	public void destroy() {
		// try {
		// sleep(5000);
		// } catch (InterruptedException e1) {
		// // TODO Auto-generated catch block
		// e1.printStackTrace();
		// }
		trayIcon.removeActionListener(listener);
		tray.remove(trayIcon);
		System.exit(0);
	}

	public void setToolTip(String tooltip) {
		trayIcon.setToolTip(tooltip);
	}

	public void displaySimpleMessage(String caption, String message) {
		if (showPopups)
			trayIcon.displayMessage(caption, message, TrayIcon.MessageType.NONE);

	}

	public void displayInfoMessage(String caption, String message) {
		trayIcon.displayMessage(caption, message, TrayIcon.MessageType.INFO);
	}

	public void displayWarnMessage(String caption, String message) {
		trayIcon.displayMessage(caption, message, TrayIcon.MessageType.WARNING);
	}

	public void displayErrorMessage(String caption, String message) {
		trayIcon.displayMessage(caption, message, TrayIcon.MessageType.ERROR);
	}

	public synchronized int confirmOutlookContactRemoval(String contact) {
		Object[] options = { "Yes", "No", "Yes, don't ask next time" };
		int confirm = JOptionPane.showOptionDialog(gooOutSettings, "Contact "
				+ contact + " was deleted in Google. Remove it from Outlook?",
				"GooOutSync Contact removal warning",
				JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE,
				null, options, options[1]);
		return confirm;

	}

	public synchronized int confirmGoogleContactRemoval(String contact) {
		Object[] options = { "Yes", "No", "Yes, don't ask next time" };
		int confirm = JOptionPane.showOptionDialog(gooOutSettings, "Contact "
				+ contact + " was deleted in Outlook. Remove it from Google?",
				"GooOutSync Contact removal warning",
				JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE,
				null, options, options[1]);
		return confirm;

	}

	@Override
	public void run() {
		if (SystemTray.isSupported()) {
			setLookAndFeel();
			// get the SystemTray instance

			tray = SystemTray.getSystemTray();
			// load an image
			trayImage = createImage(trayIconPath, "TrayIcon");
			createMenu();

			// trayIcon = new TrayIcon(trayImage, "Tray Demo", popup);
			trayIcon = new JXTrayIcon(trayImage);
			trayIcon.setImageAutoSize(true);
			trayIcon.setJPopuMenu(popup);
			// set the TrayIcon properties
			trayIcon.addActionListener(trayListener);
			// ...
			// add the tray image
			try {
				tray.add(trayIcon);
			} catch (AWTException e) {
				logger.fatal(e.getMessage());
			}
			// ...
		} else {
			// disable tray option in your application or
			// perform other actions

		}
		try {
			gooOutSettings = new GooOutSettings(this);
		} catch (GooOutSyncException e) {
			gooOutSync.setSyncFailed(true);
			setSyncFailIcon();
			displayErrorMessage("GooOutSync Failed to load settings",
					e.getMessage());
			setToolTip("GooOutSync Failed to load settings: " + e.getMessage());
			logger.error("GooOutSync Failed to load settings", e);
		}
		// ...
		// some time later
		// the application state has changed - update the image
		if (trayIcon != null) {
			// trayIcon.setImage(updatedImage);
		}
		// ...
		syncTask = new SyncTask();
		// Set up timer to drive animation events.
		timer = new Timer(timeBetweenSync, this);
		// timer.setInitialDelay(pause);
		timer.start();

		// Start loading the images in the background.
		// syncTask.execute();

	}

	protected ImageIcon createImageIcon(String path, String description) {
		java.net.URL imgURL = getClass().getResource(path);
		if (imgURL != null) {
			return new ImageIcon(imgURL, description);
		} else {
			System.err.println("Couldn't find file: " + path);
			return null;
		}
	}

	// Background task for loading images.

	@Override
	public void actionPerformed(ActionEvent arg0) {
		logger.info("Timer action performed...");
		// If still loading, can't animate.
		// if (!syncTask.isDone()) {
		if (gooOutSync.isInSync()) {
			logger.info("BUT previous syncTask IS NOT DONE yet. Cancelling...");
			return;
		}
		logger.info("syncTask IS DONE executing syncTask...");
		syncTask = new SyncTask();
		syncTask.execute();
	}

	private class SyncTask extends SwingWorker<Void, Void> {
		@Override
		public Void doInBackground() {
			logger.info("Entering background syncTask");
			gooOutSyncStatus = GooOutSyncStatus.getGooOutSyncStatus();

			gooOutSync.setInSync(true);
			startSyncIcon();
			displaySimpleMessage("GooOutSync", "Starting synchronization...");
			setToolTip("GooOutSync is synchronizing...");
			try {
				gooOutSync.retrieveContacts();
				logger.info("Entering syncNow from syncTask");
				gooOutSync.syncNow();
				logger.info("Finished syncNow in syncTask");
				gooOutSync.cleanContacts();
			} catch (GooOutSyncException e) {
				gooOutSync.setSyncFailed(true);
				setSyncFailIcon();
				displayErrorMessage("GooOutSync Failed", e.getMessage());
				setToolTip("Last sync failed with error: " + e.getMessage());
				logger.info("Failed in sync with message", e);
			} catch (ServiceException e) {
				gooOutSync.setSyncFailed(true);
				setSyncFailIcon();
				displayErrorMessage("GooOutSync Failed", e.getMessage());
				setToolTip("Last sync failed with error: " + e.getMessage());
				logger.info("Failed in sync with message", e);
			} catch (IOException e) {
				gooOutSync.setSyncFailed(true);
				setSyncFailIcon();
				displayErrorMessage("GooOutSync Failed", e.getMessage());
				setToolTip("Last sync failed with error: " + e.getMessage());
				logger.info("Failed in sync with message", e);
			} catch (InterruptedException e) {
				gooOutSync.setSyncFailed(true);
				setSyncFailIcon();
				displayErrorMessage("GooOutSync Failed", e.getMessage());
				setToolTip("Last sync failed with error: " + e.getMessage());
				logger.info("Failed in sync with message", e);
			}
			logger.info("Returning...");
			return null;

		}

		@Override
		public void done() {
			logger.info("Setting sync to false");
			gooOutSync.setInSync(false);
			logger.info("Done executed");
			if (!gooOutSync.getSyncFailed()) {
				displaySimpleMessage("GooOutSync",
						"Finished synchronization...\nSynced "
								+ gooOutSyncStatus.getSyncedContacts()
								+ " contacts");

				setToolTip("All contacts are in sync.\nGoogle Total Contacts: "
						+ gooOutSyncStatus.getGoogleContacts()
						+ "\nOutlook Total Contacts: "
						+ gooOutSyncStatus.getOutlookContacts()
						+ "\nTotal Different Contacts: "
						+ gooOutSyncStatus.getDifferentContacts()
						+ "\nTotal Synced: "
						+ gooOutSyncStatus.getSyncedContacts()
						+ "\nGoogle Created: "
						+ gooOutSyncStatus.getCreatedGoogleContacts()
						+ "\nGoogle Modified: "
						+ gooOutSyncStatus.getModifiedGoogleContacts()
						+ "\nGoogle Deleted: "
						+ gooOutSyncStatus.getDeletedGoogleContacts()
						+ "\nOutlook Created: "
						+ gooOutSyncStatus.getCreatedOutlookContacts()
						+ "\nOutlook Modified: "
						+ gooOutSyncStatus.getModifiedOutlookContacts()
						+ "\nOutlook Deleted: "
						+ gooOutSyncStatus.getDeletedOutlookContacts());
			}
			if (!isInSnooze) {
				if (!gooOutSync.getSyncFailed())
					finishSyncIcon();
				logger.info("Restarting timer...");
				timer.restart();
			} else {
				setSnoozeIcon();
				logger.info("Going back to snooze...");
			}
		}
	}

	public void deleteGoogleContactsWithOutlookID() {
		if (gooOutSync.isInSync()) {
			logger.info("Attempted cleanup, while sync running. Cancelling...");
			displaySimpleMessage("GooOutSync",
					"Cleanup not possible while sync is running...");
			return;
		}
		CleanTask cleanTask = new CleanTask();
		cleanTask.execute();
	}

	private class CleanTask extends SwingWorker<Void, Void> {
		@Override
		public Void doInBackground() {
			logger.info("Entering background CleanTask");
			gooOutSync.setInSync(true);
			startSyncIcon();
			displaySimpleMessage("GooOutSync", "Starting cleanup...");
			// setToolTip("GooOutSync is synchronizing...");
			try {
				gooOutSync.deleteGoogleContactsWithOutlookIDandCleanOutlook();
			} catch (GooOutSyncException e) {
				gooOutSync.setSyncFailed(true);
				setSyncFailIcon();
				displayErrorMessage("GooOutSync Failed", e.getMessage());
				setToolTip("Last sync failed with error: " + e.getMessage());
				logger.info("Failed in sync with message", e);
			}
			logger.info("Returning...");
			return null;

		}

		@Override
		public void done() {
			logger.info("Setting sync to false");
			gooOutSync.setInSync(false);
			logger.info("Done executed");
			displaySimpleMessage("GooOutSync", "Finished cleanup...");
			if (isInSnooze)
				setSnoozeIcon();
			else
				finishSyncIcon();
			setToolTip("Cleanup performed. Waiting for next sync.");

		}
	}

	public void setTimerDelay(Integer delay) {
		timer.setDelay(delay);
		timer.restart();
	}
}
